<?php
/**
 * MySQL数据库驱动，基于PDO
 *
 * @author zhoujing
 */
class Database
{
    private $connect;
    private $config;
    private static $_segment = array();
    
    private function __construct($segment = 'default')
    {
    	$config = array();
    	
    	if(is_string($segment))
    	{
    		$config = __Gvar::$config->get($segment, 'db');
    	}
    	
    	if(is_array($segment))
    	{
    		$config = $segment;
    	}
    	
        $this->config = $config;
        $this->connect();
    }
    
    public static function init($segment = 'default')
    {
    	if(!is_string($segment) && !is_array($segment))
    	{
    		sys_error('数据库抽象层实例化参数错误，参数必须为字符串或者数组类型');
    	}
    	
    	if(is_string($segment))
    	{
    		if(isset($_segment[$segment]))
    		{
    			return $_segment[$segment];
    		}
    		
    		$_segment[$segment] = new Database($segment);
    		
    		return $_segment[$segment];
    	}
    	
    	if(is_array($segment))
    	{
    		return new Database($segment);
    	}
    }
    
    public function __destruct()
    {
    	$this->close();
    }
    
    /**
     * 初始化并连接数据库
     * 
     * @param array $this->config(
     *  'db_type'   =>  '',     //数据库类型，默认为mysql
     *  'db_host'   =>  '',     //主机地址
     *  'db_port'   =>  '',     //主机端口，默认为3306
     *  'db_name'   =>  '',     //数据库名
     *  'db_user'   =>  '',     //用户名
     *  'db_pwd'    =>  '',     //密码
     *  'db_charset'=>  '',     //数据库连接字符集，默认为utf8
     *  'db_persist' =>  ''     //是否长连接，默认为FALSE
     * ); 
     */
    private function connect()
    {    
        if(!isset($this->config['db_charset']) || empty($this->config['db_charset']))
        {
        	$this->config['db_charset'] = 'utf8';
        }
        
        if(!isset($this->config['db_persist']) || empty($this->config['db_persist']))
        {
        	$this->config['db_persist'] = FALSE;
        }
       
        $dsn = $this->config['db_type'].':host='.$this->config['db_host'].';port='.$this->config['db_port'].';dbname='.$this->config['db_name'];
       
        $driver_opt = array(
            PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES '.$this->config['db_charset'], 
			PDO::ATTR_PERSISTENT => $this->config['db_persist']
        );
		
		//PDO连接异常处理
		try
		{
			$this->connect = new PDO($dsn, $this->config['db_user'], $this->config['db_pwd'], $driver_opt);
		}
		catch(PDOException $e)
		{
			sys_error('PDO连接数据库发生异常，原因 {1} ', $e->getMessage());
		}
    }
    
    public function close()
    {
    	$this->config = NULL;
    	$this->connect = NULL;
    }
	
	/**
	 * 根据传递的数组生成合适的SQL语句部分
	 *
	 * @param array $param 数组类型，键代表字段名字，值代表值 
	 * @return string 返回类似`a`='b',`c`='d'格式的数据
	 */
	private function _map_param($param)
	{
		if(!is_array($param) || empty($param))
		{
			return '';
		}
		
		$arr = array();
		foreach($param as $key=>$value)
		{
			$arr[] = "`{$key}` = '{$value}'";
		}
		
		return join(',', $arr);
	}
	
	/**
	 * 生成where条件部分
	 * array(
	 * 'a' => 'sth',
	 * 'c' => 'sth',
	 * 'or b <>'=> 'sth',
	 * )
	 * 以上数组条件对应 `a`='sth' AND `c`='sth' OR `b`<>'sth'
	 *
	 * @param array $param 数组类型，键代表字段名字，值代表值 
	 * @return string
	 */
	private function _where($param)
	{
		if(!is_array($param) || empty($param))
		{
			return '';
		}
		
		$arr = array();
		$i = 1;
		foreach($param as $key=>$value)
		{
			$key = trim($key);
			
			//是否添加AND前缀
			$is_and = true;
			if(preg_match('/^or\s+/i', $key))
			{
				$is_and = false;
			}
			if($i == 1)
			{
				$is_and = false;
			}
			
			if($is_and == true)
			{
				$key = "AND {$key} ";
			}
			
			if(!preg_match('/(\s<>|\slike|=)/i', $key))
			{
				$key = "{$key} = ";
			}
			
			$arr[] = "{$key}'{$value}'";
			$i++;
		}
		
		return join(' ', $arr);
	}
    
    /**
     * 直接执行sql语句，根据sql语句的类型返回不同的结果
     * 
     * @param string $sql 标准sql语句
     * @return mixed 
     */
    public function query($sql)
    {
        if(is_string($sql) && !empty($sql))
        {
            if(preg_match('/^\s*SELECT/i', $sql))
            {
            	//这是一条select查询，需要返回关联结果集
            	$pdostate = $this->connect->query($sql);
            	if($pdostate === false)
            	{
            		sys_error('PDO执行SQL语句 [ {1} ] 失败，请检查语句！', $sql);
            	}
                return $pdostate->fetchAll(PDO::FETCH_ASSOC);
            }
            
			if(preg_match('/^\s*(INSERT|UPDATE|DELETE)/i', $sql, $match))
            {
            	//返回被影响的行数
                $pdostate = $this->connect->exec($sql);
                if($pdostate === false)
                {
                	sys_error('PDO执行SQL语句 [ {1} ] 失败，请检查语句！', $sql);
                }
                //非查询的情况返回受影响的行数
                return $pdostate;
            }
        }
        
		sys_error('系统无法解析 [ {1} ] 语句，请传递符合SQL语法规范的语句格式', $sql);
    }
    
    /**
     * 返回上次插入的id值
     * 
     * @return string
     */
    public function last_insert_id()
    {
    	return $this->connect->lastInsertId();
    }

    /**
     * 插入数据
     * 
     * @param string $table_name 表名
     * @param array 名值对数组，跟数据库对应
     * 
     * @return int lastInsertId
     */
    public function insert($table_name, $param)
    {
		if(!is_string($table_name))
		{
			return false;
		}
		$sql = "INSERT INTO `{$table_name}` SET ".$this->_map_param($param);
        return $this->query($sql);
    }
    
    /**
     * 更新数据
     * 
     * @param string $table_name 表名
     * @param array 名值对数组，键值对
     * @param array WHERE条件，键值对
     * 
     * @return int 返回受影响的行数
     */
    public function update($table_name, $param, $where = array())
    {
        if(!is_string($table_name))
		{
			return false;
		}
		
        $sql = "UPDATE `{$table_name}` SET ".$this->_map_param($param);
        if(!empty($where))
        {
            $sql .= " ".$this->_where($where);
        }
        return $this->query($sql);
    }
    
    /**
     * 删除数据(慎用)
     * 
     * @param string $table_name 表名
     * @param string WHERE条件，键值对
     * 
     * @return int 返回删除的行数
     */
    public function delete($table_name, $where = '')
    {
        $sql = "DELETE FROM `{$table_name}`";
        if(!empty($where)){
            $sql .= " ".$this->_where($where);
        }
        return $this->query($sql);
    }
    
    /**
     * 获取数据集
	 *
	 * @param string $table_name 表名
	 * @param array $where 查询条件
	 * @param string $fields 字段名 'a,b,c'
	 * @param string $order 根据什么排序，如字符串：'id desc'
	 * @param int $offset 记录开始位置
	 * @param int $count 每页记录数
	 *
	 * @return array 多维数组
     */
    public function gets($table_name, $where = array(), $fields = '*', $order = '', $offset = 0, $count = 0)
    {
		$sql = "SELECT {$fields} FROM `{$table_name}`";
		
		if(!empty($where)){
			$sql .= " WHERE ".$this->_where($where);
		}
		
		if(!empty($order))
		{
			$sql .= " ORDER BY {$order}";
		}
		
		if($count != 0)
		{
			$sql .= " LIMIT {$offset},{$count}";
		}
		
		$set = $this->query($sql);
		
		return $set ? $set : FALSE;
	}
	
	/**
     * 获取单条记录
	 *
	 * @param string $table_name 表名
	 * @param array $where 查询条件
	 * @param string $fields 字段名 'a,b,c'
	 * @param string $order 根据什么排序，如字符串：'id desc'
	 *
	 * @return array 多维数组
     */
	public function get($table_name, $where = array(), $fields = '*', $order = '')
	{
		$sql = "SELECT {$fields} FROM `{$table_name}`";
		
		if(!empty($where)){
			$sql .= " WHERE ".$this->_where($where);
		}
		
		if(!empty($order))
		{
			$sql .= " ORDER BY {$order}";
		}
		
		$sql .= " LIMIT 1";
		
		$set = $this->query($sql);
		if($set && is_array($set))
		{
			return $set[0];
		}
		
		return FALSE;
	}
    
    /**
     * 获取PDO对象实例
     */
    public function pdo()
    {
        return $this->connect;
    }    
}